:- dynamic(branch/5, extra_point/1, t/1).
%:- ensure_loaded(circuit).

debug_output :- true.

printf([]) :- nl.
printf([Head | Tail]) :-
	write(Head), write(' '), printf(Tail).
debug(List) :-
	debug_output -> 
		append(['Debug: '], List, Out), printf(Out);
		true.

output_connections :-
	debug(['output_connections']),
	branch(Value, First, Second, N, Type),
	retract(branch(Value, First, Second, N, Type)),
	retract(branch(Value, Second, First, N, Type)),
	printf([Value, First, Second, Type]),
	fail; true.

solve(X, Y) :-
	retractall(extra_point(_)),
	assertz(extra_point(X)), assertz(extra_point(Y)),
	fill_branch;
	simplify.

query(R, X, Y) :-
	retractall(extra_point(_)),
	assertz(extra_point(X)), assertz(extra_point(Y)),
	fill_branch;
	simplify, branch(R, X, Y, _, _), !;
	write('I can''t get solution...'), nl.

fill_branch :-
	debug(['fill_branch']),
	retractall(branch(_, _, _, _, _)), retractall(t(_)),
	assertz(t(0)), fail;
	connection(R, X, Y, Type), t(N), N1 is N + 1, retract(t(N)),
	debug(['fill_branch:', 'connection: ', R, X, Y, Type]),
	assertz(t(N1)), assertz(branch(R, X, Y, N, Type)),
	assertz(branch(R, Y, X, N, Type)), fail.

simplify :-
	simplify1;
	simplify2;
	output_connections.%;
	%calcnbr(2).

simplify1 :-
	retractall(t(1)), 
	parallel;
	sequential;
	cut;
	t(1), simplify1.

simplify2 :-
	retractall(t(2)), star, simplify1;
	t(2), simplify2.

calcnbr(X) :-
	retractall(t(_)), assertz(t(0)), fail;
	branch(_, _, _, _, _), t(N), N1 is N + 1,
	retract(t(N)), assertz(t(N1)), fail;
	t(N), X = N.

seq(Val1, Val2, capacitor, Result) :-
	Result is Val1 * Val2 / (Val1 + Val2).
seq(Val1, Val2, Type, Result) :-
	\=(Type, capacitor), Result is Val1 + Val2.

par(Val1, Val2, capacitor, Result) :-
	Result is Val1 + Val2.
par(Val1, Val2, Type, Result) :-
	\=(Type, capacitor), Result is Val1 * Val2 / (Val1 + Val2).

sequential :-
	branch(R1, X, Y, N1, Type), branch(R2, Y, Z, N2, Type), N1 \== N2, simple2(Y),
	retract(branch(R1, X, Y, N1, Type)), retract(branch(R2, Y, Z, N2, Type)),
	retract(branch(R1, Y, X, N1, Type)), retract(branch(R2, Z, Y, N2, Type)),
	seq(R1, R2, Type, R),
	assertz(branch(R, X, Z, N1, Type)), assertz(branch(R, Z, X, N1, Type)),
	assertz(t(1)), assertz(t(2)), fail.

simple2(X) :-
	branch(_, X, _, N1, _), branch(_, X, _, N2, _), N1 \== N2,
	branch(_, X, _, N3, _), N1 \== N3, N2 \== N3, !, fail;
	extra_point(X), !, fail;
	true.

parallel :- 
	branch(R1, X, Y, N1, Type), branch(R2, X, Y, N2, Type), N1 \== N2,
	retract(branch(R1, X, Y, N1, Type)), retract(branch(R2, X, Y, N2, Type)),
	retract(branch(R1, Y, X, N1, Type)), retract(branch(R2, Y, X, N2, Type)),
	par(R1, R2, Type, R),
	assertz(branch(R, X, Y, N1, Type)), assertz(branch(R, Y, X, N1, Type)),
	assertz(t(1)), assertz(t(2)), fail.

cut :-
	branch(_, X, X, _, _), retractall(branch(_, X, X, _, _)),
	assertz(t(1)), assertz(t(2)), !, fail; % удаление петель
	branch(_, X, _, _, _), simple1(X),
	retractall(branch(_, X, _, _, _)), retractall(branch(_, _, X, _, _)),
	assertz(t(1)), assertz(t(2)),
	fail. % удалирование изолированных соединений

simple1(X) :-
	branch(_, X, _, N1, _), branch(_, X, _, N2, _), N1 \== N2, !, fail;
	extra_point(X), !, fail;
	true.

evaluate(R1, R2, R3, capacitor, R12, R23, R13) :-
	RT is R1 + R2 + R3,
	R12 is (R1 * R2) / RT, R23 is (R2 * R3) / RT, R13 is (R3 * R1) / RT.
evaluate(R1, R2, R3, Type, R12, R23, R13) :-
	\=(Type, capacitor),
	RT is R1 * R2 + R2 * R3 + R1 * R3,
	R12 is RT / R3, R23 is RT / R1, R13 is RT / R2.

star :-
	branch(R1, X1, Y, N1, T), branch(R2, X2, Y, N2, T), N1 \== N2,
	branch(R3, X3, Y, N3, T), N1 \== N3, N2 \== N3, simple3(Y),
	retract(branch(R1, X1, Y, N1, T)), retract(branch(R1, Y, X1, N1, T)),
	retract(branch(R2, Y, X2, N2, T)), retract(branch(R2, X2, Y, N2, T)),
	retract(branch(R3, X3, Y, N3, T)), retract(branch(R3, Y, X3, N3, T)),
	evaluate(R1, R2, R3, T, R12, R23, R13),
	%RT is R1 * R2 + R2 * R3 + R1 * R3,
	%R12 is RT / R3, R23 is RT / R1, R13 is RT / R2,
	assertz(branch(R12, X1, X2, N1, T)), assertz(branch(R12, X2, X1, N1, T)),
	assertz(branch(R13, X1, X3, N2, T)), assertz(branch(R13, X3, X1, N2, T)),
	assertz(branch(R23, X2, X3, N3, T)), assertz(branch(R23, X3, X2, N3, T)).

simple3(X) :-
	branch(_, X, _, N1, _), branch(_, X, _, N2, _), N1 \== N2,
	branch(_, X, _, N3, _), N1 \== N3, N2 \== N3,
	branch(_, X, _, N4, _), N1 \== N4, N2 \== N4, N3 \== N4, !, fail;
	extra_point(X), !, fail;
	true.
